//
//  main.c
//  Array
//
//  Created by admin on 2019/7/20.
//  Copyright © 2019 猫爷爷的酱醋茶. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
/*
 
1. 数组：
    1）元素：数组中的每一个小空间，就叫做数组的元素。本质其实就是一个普通类型的变量，也有自己的地址。
    2）下标/索引：为了区分数组中的每一个元素，从0开始，依次递增
    3）长度：数组中的元素，也是最多可存储多少个元素。在声明时，必须要指定数组的长度。可以是常量、变量、表达式（其结果）、字符（其ASCII码）
 
 有多个类型相同且意义相同的数据需要存储的时候，使用数组。
 
 2. 数组的默认值会存在垃圾值的情况。
    数组的初始化/赋值
    1）int arr[3] = {};
    2）int arr[] = {10,20,30};
    3）int arr[3] = {10}; 那么arr[0] = 10；其余的都为0；
    4）int arr[3] = {[1] = 10,[2] = 20}; 那么其余的都为0；
    5）如果数组的长度是个变量，则不能初始化，即int arr[num];
 
 
 3. 数组在内存中的存储：
    1）声明一个数组，在内存中从高字节向低字节申请连续的（数组长度*每个元素的字节数）个字节空间；
    2）从下标为0的元素开始，向低地址存储。
 
    数组的地址，是低地址的数组元素的地址。即，数组[0]的地址。
        重点：数组名就代表数组的地址。 例：TheArrayAddress();
 
 4. 计算数组的长度：
     1）因为数组中每一个元素的类型相同，所以数组的每一个元素占用的字节空间一样；
     2）所以可以用sizeof运算符，计算出数组的字节数，再除以每个元素所占的字节空间，就可以得到数组的长度。
        a. sizeof(数组名) / sizeof(数组名[0])
        b. sizeof(数组名) / sizeof(数据类型)
    例：TheArrayLength();
 
 
 5. 函数的参数
    1）为函数传参的过程就是一个赋值的过程，是将实参赋值给形参，在作用域的影响下，函数里面参数值的变化，实参不受影响，
        例：TheFunctionParameter();   应用：TheFunctionTest1();
    2）但是，函数参数是数组的时候，函数内部参数值的变化，将直接作用于实参，因为函数声明的参数，是数组的指针地址。
        例：TheArrayChange(); 应用：TheFunctionParameterIsArray();
    3）当数组作为函数的参数的时候，即function(int arr[]) 在函数内部无法使用sizeof()计算参数数组的字节数，得到的永远是8字节。
        因为系统加载函数，声明这个数组参数时，并没有去真正创建数组，而是声明一个用来存储数组地址的一个指针变量，这个指针变量在内存中占8字节。
        所以，在函数内部使用sizeof()函数得到的永远是8字节，造成了数组数据长度丢失。
        这种参数传递，也叫做地址传递。
     解决办法：
        让调用者把函数的长度以参数的形式传递，即function(int arr[],int length);
 
 6. 需要掌握的几种算法
    1）找到其中的最大最小值
    2）求其中元素的累加和。 例：TheArrayLength();
    3）求所有数的平均数；
    4）排序算法：
        a. 选择排序 始终用第i个来与剩下的逐个比较，符合条件的交换值 例：SelectSort();
        b. 冒泡排序 始终与相邻的数 用第i个与i+1的数比较，符合条件的交换值， 例：BubbleSort();
    5）二分查找法，例：BinarySeach();
    6）翻转数组，例：FlipArray();

 7. 二维数组的长度，一般认为是单元格的个数，即行*列
    1）声明一个二维数组，必须指定行数和列数，可以是常量、变量、宏、表达式，也可以是0和1；
    2）二维数组的初始化：
        全部初始化
            a.
                int array[3][4] =
                 {
                    {11,12,13,14},
                    {15,16,17,18},
                    {19,20,21,22}
                 };
                int array[3][4] = {0};
            b.  上面定义可以省略行数，编译器会自动确认行数。即，array[][4];
            c. int array[][4] = {11,12,13,14,15,16,17,18,19,20,21,22};
 
         部分初始化
            int array[3][4] = {{10},{20},{30}}; //其他元素为0；
            int array[3][4] = {[1] = {10,20,30,40},[2] = {50,60,70,80}}; //给指定的行初始化
            int array[3][4] = {[1][2] = 10,[2][3] = 20};//给指定的元素赋值,其余为0;
 
 8. 二维数组在内存中，第0行存在低字节。二维数组的地址，就是低字节的地址。
    二维数组的每一行，其实是一个一维数组。所以，可以用printf("%p",array[0]),不加&取地址符，取到二维数组的地址
    二维数字名，也代表这个二维数组的地址
    例：TwoDimensionalArrayAddress();
 
 9. 计算二维数组的长度和行列数
    长度： sizeof(array)/sizeof(变量类型)、sizeof(array)/sizeof(array[0][0])
    行rows：sizeof(array)/sizeof(array[0])
    列cols：sizeof(array[0])/sizeof(变量类型)、sizeof(array[0])/sizeof(array[0][0])
 
    二维数组作为行函数参数时，需要同时传入行数和列数
    void TwoDimensionalArray(int rows,int cols,int array[][cols]);
 
 10. 字符数组的遍历
    char chs[4];
    因为字符只占一个字节，故字符数组的长度为 sizeof(chs);
 
 
 
. 游戏
    随机产生一个双色球的号码
        1~33 随机产生6个不重复的数
        1~16 产生1个随机数
    例：TwoColorBall();
 
 */

//数组的遍历
void ArrayTraversal(void);

//数组的地址
void TheArrayAddress(void);

//计算数组的长度
void TheArrayLength(void);

//函数的传值
void TheFunctionParameter(int num);

//函数传值的测试
void TheFunctionTest1(void);

//数组作为函数参数传值的情况
void TheArrayChange(int array[],int length);

//数组删除的函数传值测试
void TheFunctionParameterIsArray(void);

//双色球
void TwoColorBall(void);

//判断数组中是否包含这个数
int WhetherToInclude(int array[],int length,int key);

//从大到小排序
void SortFromBigToSmall(void);

//选择排序
void SelectSort(void);

//冒泡排序
void BubbleSort(void);

//二分查找法
void BinarySearch(void);

//翻转数组
void FilpArray(void);

//二维数组的地址
void TwoDimensionalArrayAddress(void);

int main(int argc, const char * argv[]) {
    

    TwoDimensionalArrayAddress();
    
    return 0;
}

//二维数组地址
void TwoDimensionalArrayAddress()
{
    
    int array[3][4] =
    {
        {11,12,13,14},
        {15,16,17,18},
        {19,20,21,22}
    };
    
    printf("二维数组array[3][4]的地址为%p\n",&array[3][4]);
    //因为二维数组每一行，其实为一个一维数组，所以取地址还可以直接为，而不需要加取地址符
    printf("二维数组array[3][4]的地址为%p\n",array[0]);
    //二维数组名，就代表这个二维数组的地址
    printf("二维数组array[3][4]的地址为%p\n",array);
    
    int length = sizeof(array)/sizeof(array[0][0]);
    int rows = sizeof(array)/sizeof(array[0]);
    int cols = sizeof(array[0])/sizeof(array[0][0]);
    
    printf("二维数组array[3][4]的长度为%d\n",length);
    printf("二维数组array[3][4]有%d行\n",rows);
    printf("二维数组array[3][4]有%d列\n",cols);
    
}


//翻转数组
void FilpArray(void)
{
    int array[] = {134,145,187,234,267,289,312,335,345,378,387};
    int number = sizeof(array)/sizeof(array[0]) - 1;
    for (int i = 0; i < (number/2); i++) {
        
        array[i]          = array[i]^array[number - i];
        array[number - i] = array[i]^array[number - i];
        array[i]          = array[i]^array[number - i];
        
    }
    for (int i = 0; i < number + 1; i++)
    {
        printf("%d  ",array[i]);
    }
    printf("\n");
    
    
}

//二分查找法
void BinarySearch(void)
{
    int array[] = {134,145,187,234,267,289,312,335,345,378,387};
    int min = 0;
    int max = sizeof(array)/sizeof(array[0]) - 1;
    int mid = max / 2;
    printf("请输入你要查找的数字：\n");
    int number = 0;
    scanf("%d",&number);
    while (array[mid] != number)
    {
        if (array[mid] > number)
        {
            max = mid - 1;
        }
        else if(array[mid] < number)
        {
            min = mid + 1;
         
        }
        mid = (min + max)/2;
    }
    printf("你说需要找的数字在下标为%d\n",mid);
    
}

//冒泡排序
void BubbleSort(void)
{
    int array[] = {67,23,15,73,34,02,23,45,12,34,89,84,32};
    int arrayLength = sizeof(array)/sizeof(array[0]);
    for (int i = 0; i < arrayLength - 1; i++)
    {
        for (int j = 0; j < arrayLength - i - 1; j++)
        {
            if (array[j] < array[j+1])
            {
                array[j]   = array[j] ^ array[j+1];
                array[j+1] = array[j] ^ array[j+1];
                array[j]   = array[j] ^ array[j+1];
            }
        }
    }
    for (int i = 0; i < arrayLength; i++)
    {
        printf("%d  ",array[i]);
    }
    printf("\n");
    
}

//选择排序
void SelectSort(void)
{
    int array[] = {67,23,15,73,34,02,23,45,12,34,89,84,32};
    int arrayLength = sizeof(array)/sizeof(array[0]);
    for (int i = 0; i < arrayLength - 1; i++)
    {
        for (int j = i + 1; j < arrayLength; j++)
        {
            if (array[i] < array[j])
            {
                //交换彼此的值
                array[i] = array[i] ^ array[j];
                array[j] = array[i] ^ array[j];
                array[i] = array[i] ^ array[j];
            }
        }
    }
    
    for (int i = 0; i < arrayLength; i++)
    {
        printf("%d  ",array[i]);
    }
    printf("\n");
}

//从大到小排序
void SortFromBigToSmall(void)
{
    
    int array[] = {67,23,15,73,34,02,23,45,12,34,89,84,32};
    int arrayLength = sizeof(array)/sizeof(array[0]);
    int maxNumber = INT32_MIN;
    for (int i = 0; i < arrayLength; i++)
    {
        int randomNumber = array[i];
        maxNumber = INT32_MIN;
        for (int j = i; j < arrayLength;j++)
        {
            maxNumber = array[j] > maxNumber ? array[j] : maxNumber;
        }
        
        for (int k = i; k < arrayLength; k++)
        {
            if (maxNumber == array[k]) {
                array[i] = maxNumber;
                array[k] = randomNumber;
                break;
            }
        }
    }

    for (int i = 0; i < arrayLength; i++)
    {
        printf("%d  ",array[i]);
    }
    printf("\n");
    
}

//双色球
void TwoColorBall(void)
{
    
    int ballNumber[6] = {};
    int arrayLength = sizeof(ballNumber)/sizeof(ballNumber[0]);
    
    for (int i = 0; i < 6; )
    {

        int number = arc4random_uniform(33) + 1;
        
        if (WhetherToInclude(ballNumber, arrayLength, number))
        {
            ballNumber[i] = number;
            i++;
           
        }else{
            printf("%d重复出现\n",number);
        }
    }
    
    for (int i = 0; i < arrayLength; i++)
    {
        printf("%d  ",ballNumber[i]);
    }
    printf("\n");
    
    
    
   
}
//判断数组中是否包含这个数
int WhetherToInclude(int array[],int length,int key)
{
    for (int i = 0; i < length; i++)
    {
        if (key == array[i])
        {
            return 0;
        }
    }
    
    return 1;
}

//数组删除的函数传值测试
void TheFunctionParameterIsArray(void)
{
    //数组作为函数参数，里面的值会发生变化
    int arr[] = {10,35,67,23,67,12};
    int length = sizeof(arr)/sizeof(arr[0]);
    TheArrayChange(arr, length);
    for(int i = 0;i < length;i++)
    {
        printf("arr[%d] = %d\n",i,arr[i]);
    }
}


//数组作为函数参数传值的情况
void TheArrayChange(int array[],int length)
{
    
    for(int i = 0;i < length;i++)
    {
        array[i] = 100;
    }
    
}

//函数传值的测试
void TheFunctionTest1(void)
{
    
    //实参赋值给形参
    int num = 10;
    TheFunctionParameter(num);//只是赋值，但并不影响实参。
    printf("num = %d\n",num);// num = 10.
    
}

//函数的传值
void TheFunctionParameter(int num)
{
    //作用域只在函数体内1
    num++;
    num++;
    num++;
}

//计算数组的长度
void TheArrayLength(void)
{
    int array[] = {1,23,4,15,56,346,998,234,23,34,5,636,434,5,23,123,78};
    int length = sizeof(array) / sizeof(array[0]);
    //int length = sizeof(array) / sizeof(int);
    
    for (int i = 0; i < length; i++) {
        printf("array[%d] = %d\n",i,array[i]);
    }
    
    int maxNumber = INT32_MIN;
    int minNumber = INT32_MAX;
    //找出其中的最大值
    for (int j = 0; j < length; j++) {
        
        maxNumber = (maxNumber < array[j] ? array[j] : maxNumber );
        
    }
    printf("数组array中最大值为:%d\n",maxNumber);
    
    //找出其中的最小值
    for(int k = 0; k < length; k++)
    {
        minNumber = minNumber > array[k] ? array[k] : minNumber;
    }
    printf("数组array中最小值为:%d\n",minNumber);
    
    
}


//数组的地址
void TheArrayAddress(void)
{
    int arr[3] = {10,20,30};
    printf("数组的地址为数组[0]的地址：%p\n",&arr[0]);
    printf("数组的地址名，也为数组的地址：%p\n",arr);
    
    
}

//数组的遍历
void ArrayTraversal(void)
{
    int arr[] = {10,20,30};
    for(int i = 0;i < 3;i++){
        printf("arr[%d] = %d\n",i,arr[i]);
    }
}
